package com.fm.financemanage.component;

import com.alibaba.fastjson.JSONObject;


import com.fm.financemanage.component.RedisComponent;
import com.fm.financemanage.component.TokenComponent;
import com.fm.financemanage.constant.RedisKeyEnum;
import com.fm.financemanage.constant.ResponseEnum;
import com.fm.financemanage.constant.UserConstants;
import com.fm.financemanage.constant.annotation.RateLimit;
import com.fm.financemanage.constant.annotation.Require;
import com.fm.financemanage.model.vo.UserTokenVo;
import com.fm.financemanage.response.Callback;
import com.fm.financemanage.response.JsonData;
import com.fm.financemanage.utils.IpUtil;
import com.fm.financemanage.utils.TextUtil;
import lombok.extern.slf4j.Slf4j;
import org.owasp.encoder.Encode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;


import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;

/**
 * @author : tony
 * @Date: 2019-02-27 11:29
 * @Description:
 */
@Slf4j
@Component
public class BaseInterceptor implements HandlerInterceptor {

    @Autowired
    private TokenComponent tokenComponent;

    @Autowired
    private RedisComponent redisComponent;

    @Autowired
    private RateLimitComponent rateLimitComponent;


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) throws Exception {
        // 如果不是映射到方法直接通过,不是访问接口，直接通过
//        System.out.println(request.getRequestURI());
        if (!(object instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) object;
        Method method = handlerMethod.getMethod();
        // 获取头部的token值
        String authorization = request.getHeader("Authorization");

        if (method.isAnnotationPresent(RateLimit.class)) {
            RateLimit rateLimit = method.getAnnotation(RateLimit.class);
            String key = rateLimit.key();
            int capacity = rateLimit.capacity();
            int replenishRate = rateLimit.replenishRate();
            int requestedTokens = rateLimit.requestedTokens();
            String message = rateLimit.message();
            if (!TextUtil.isEmpty(authorization)) {
                UserTokenVo member = tokenComponent.getMember(authorization);
                key = key + "_" + member.getUuid();
            } else {
                String ip = IpUtil.getIp(request);
                key = key + "_" + ip;
            }
            boolean allow = rateLimitComponent.isAllow(key, replenishRate, capacity, requestedTokens);
            if (!allow) {
                ResponseEnum errorRateLimit = ResponseEnum.ERROR_RATE_LIMIT;
                // 返回请求频繁提示

                responseMsg(response, errorRateLimit, message);
                return false;
            }
        }

        if (!method.isAnnotationPresent(Require.class)) {
            return true;
        }
        // 校验头部是否含有token
        if (TextUtil.isEmpty(authorization)) {
            responseMsg(response, ResponseEnum.LOST_OR_EXPIRE_TOKEN, null);
            return false;
        }

        if (method.isAnnotationPresent(Require.class)) {
            return checkWebRequest(authorization, request, response);
        }
        return true;
    }


    /**
     * 检测web端用户是否具有访问权限
     *
     * @param token
     * @return
     */
    private boolean checkWebRequest(String token, HttpServletRequest request, HttpServletResponse response) {
        try {
            // 校验token数据格式是否正确
//            UserTokenVo member = tokenComponent.getMember(token);
//            if (member == null) {
//                responseMsg(response, ResponseEnum.LOST_OR_EXPIRE_TOKEN,null);
//                return false;
//            }
            // 获取redis中的数据
            Object data = redisComponent.getValue(RedisKeyEnum.user_tokens.getName() + token);
            // redis 中不存在，则表示很久未访问，已失效，需重新获取
            if (data == null) {
                responseMsg(response, ResponseEnum.LOST_OR_EXPIRE_TOKEN, null);
//                log.error("网页端长时间未访问，token过期，验证重新登录");
                return false;
            }
            UserTokenVo userInfoVo = JSONObject.parseObject(data.toString(), UserTokenVo.class);
            request.setAttribute("uuid", userInfoVo.getUserType().equals(UserConstants.USER_TYPE_KJ) ? userInfoVo.getCompanyKey() : userInfoVo.getUuid());
            return true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }


    /**
     * 构建错误提示语
     *
     * @param response
     * @param responseEnum
     * @throws IOException
     */
    private void responseMsg(HttpServletResponse response, ResponseEnum responseEnum, String message) throws IOException {
        JsonData jsonData = Callback.fail(responseEnum.getCode(), TextUtil.isEmpty(message) ? responseEnum.getName() : message, "");
        response.setCharacterEncoding("utf-8");
        response.setContentType("application/json; charset=utf-8");
        String json = JSONObject.toJSONString(jsonData);
        PrintWriter out = response.getWriter();
        out.print(Encode.forHtmlContent(json));
        out.flush();
        out.close();
    }


}
